// Copyright 2011-2012, Vanya Davidenko.
// Используемая кодировка: UTF-8.

#ifndef LIBV_BOOST_SMART_ENUM_H_
#define LIBV_BOOST_SMART_ENUM_H_
#include <cstring>
#include <boost/integer_traits.hpp>
#include <boost/preprocessor/arithmetic/dec.hpp>
#include <boost/preprocessor/array/elem.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/comparison/less.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <libv/check.h>



// @file
// Существует 3 основных способа представления множества констант в С++:
//  - макросы 
//  - класс с определенными через static const int именованными значениями
//  - перечисления.
//
// Недостатками макросов являются 
//  - "низкоуровневость"
//  - загрязнение общего пространства имен
//  - отсутсвие типизации и необходимость проверять корректность значений.
// 
// Недостатками использования множества static const int являются:
//  - необходимость файла реализации (недопустимо в header only-библиотеках)
//  - отсутствие типизации и необходимость проверять корректность значений.
// 
// В отличие от перечисленных выше способов, перечисления обладают только одним 
// недостатком: отсутствие проверки корректности значений.
// Верным является следующий код: enum A { X = 0; }; A a = A(1);
// Проблемами являются также:
//  - неявное преобразование в int 
//  - неопределенный размер.
//
// libv smart enum не обладают перечисленными выше недостатками:
//  - допустимо использования в header only-библиотеках
//  - статическая типизация и проверка корректности значений,
// а так же обладают рядом достоинств:
//  - интерфейс соответствует интерфейсу enum (кроме макроса для объявления)
//  - возможность *явного* преобразования из/в int
//  - возможность явного преобразования из/в строку
//  - возможность задания целочисленного типа, который будет использоваться
//    для хранения значения и, как следствие, определять размер объекта
//    и проверка на этапе компиляции отсутствие переполнения данного типа
//    одним из значений
//  - наличие значения kUndefined обозначающего "отсутствие значения"
//  - возможность итерирования по всем возможным значениям (данная 
//    функциональность необходима в случаях, когда нужно вывести пользователю
//    все доступные значения, например, при использовании перечисления 
//    для хранения цвета или типа команды).
//
//
// Данная библиотека определяет 2 типа перечислений: 
//  - со значениями, задаваемыми автоматически (LIBV_DEFINE_SMART_ENUM[_STRICT])
//  - со значениями, задаваемыми пользователем 
//    (LIBV_DEFINE_SMART_ENUM_USER_VALUES[_STRICT]).
// 
//  При использовании постфикса _STRIСT последним аргументом передается
//  целочисленный тип, используемый для хранения значения, по умолчанию — int. 
// Таким образом, макрос 
//   LIBV_DEFINE_SMART_ENUM(...)
// аналогичен макросу 
//   LIBV_DEFINE_SMART_ENUM_STRICT(..., int).
//
//
//
// Рассмотрим макрос LIBV_DEFINE_SMART_ENUM_STRICT(EnumType, (A)(B)(C), int8_t),
// аналогичный по смыслу enum { A, B, C } (без учета конкретных значений).
//
// Помимо значений A, B, C определяются:
//  - kUndefined, используемое конструктором по умолчанию
//  - 2 граничных значения — kMin == kUndefined и kMax == C. Гарантируется, 
//    что все допустимые значения лежат последовательно между kMin и kMax.
// 
// Примеры использования:
//   EnumType a = EnumType::A;
//   assert(a == EnumType::A);
//
//   EnumType b = EnumType::FromString("B");
//   assert(b == EnumType::B);
//   assert(!::strcmp(b.ToString(), "B"));
//
//   EnumType c = b.Next();
//   assert(c == EnumType::С);
//
//   int c_val = c.ToInt();
//   assert(c == EnumType::FromInt(c_val));
//   assert(EnumType::IsValidValue(c_int));
//   assert(!EnumType::IsValidValue(666));
//
//   assert(EnumType::kMin == u);
//   assert(EnumType::kMax == c);
//
//   EnumType u;
//   assert(EnumType::kUndefined == u);  // порядок аргументов не имеет значения
//   assert(u == EnumType::FromString("kUndefined"));
//   assert(u == EnumType::FromString(NULL));
//   assert(u.Prev() == EnumType::D);
//   assert(u.Next() == EnumType::A);
//
//   ::std::vector<SmartEnum> v;
//   ::std::set<SmartEnum> s;
//
//   // Следующие строки вызовут исключения предусловий:
//   // EnumType error_no_valid_value = EnumType(EnumType::Impl(666));
//   // EnumType error_no_valid_int=EnumType::FromInt((int)(EnumType::kMax)+1);
//   // EnumType error_no_valid_string = EnumType::FromString("XXX");
//
//   // Следующие строки не скомпилируются.
//   // EnumType error_no_implicit_from_int_cast = EnumType(1);
//   // enum X { A }; EnumType error_no_cast_from_enums = EnumType(X::A);
//
// Макрос разворачивается примерно в следующий код:
// class EnumType {       
//   public:
//     typedef int8_t value_type;                                          
//                                                                               
//     enum Impl {                                                               
//         kUndefined,                                                           
//         kMin = kUndefined,  
//         A, B, C,
//         kMax = C
//     };                                                                        
//                                                                               
//     /* Проверка принадлежности значения области допустимых значений. */       
//     /* Сложность: O(1).                                              */       
//     static bool IsValidValue(const value_type& value) {                       
//         return kMin <= value && value <= kMax;                                
//     }                                                                         
//                                                                               
//     /* Именованный конструктор из числа, принадлежащего ОДЗ. */               
//     /* Сложность: O(1).                                      */               
//     static EnumType FromInt(const value_type& value) {                       
//         return EnumType(Impl(value)); /* конструктор проверит предусловие */ 
//     }                                                                         
//     /* Именованный конструктор из строки.        */                           
//     /* Для NULL возвращается kUndefined.         */                           
//     /* Поддерживаются значения kMin и kMax.      */                           
//     /* Сложность: не более O(n) сравнений строк. */                           
//     static EnumType FromString(const char* str) {                            
//         if ( str == NULL || !::std::strcmp(str, "kUndefined") ) {             
//             return kUndefined;                                                
//         }                                                                     
//         /* ToString никогда не выдаст "kMin" и "kMax", */                     
//         /* но вдруг пользователь так захочет.          */                     
//         if ( !::std::strcmp(str, "kMin") ) {                                  
//             return kMin;                                                      
//         }                                                                     
//         if ( !::std::strcmp(str, "kMax") ) {                                  
//             return kMax;                                                      
//         }      
//         if ( !::std::strcmp(str, "A") ) {
//             return A;
//         }
//         if ( !::std::strcmp(str, "B") ) {
//             return B;
//         }
//         if ( !::std::strcmp(str, "C") ) {
//             return C;
//         }                        
//                                                                               
//         LIBV_PRECOND_MSG(false, "Bad string value");                          
//         return EnumType();  /* недостижимо */                                
//     }                                                                         
//                                                                               
//     /* Сложность: O(1). */                                                    
//     EnumType()                                                               
//       : value_(kUndefined) {                                                  
//         assert(IsValidValue(value_));  /* для тестирования */                 
//     }                                                                         
//                                                                               
//     /* Сложность: O(1). */   
//     /*implicit*/ EnumType(const Impl& value)                                 
//       : value_(static_cast<value_type>(value)) {                              
//         LIBV_PRECOND(IsValidValue(value_));                                   
//     }                                                                         
//                                                                               
//     /* Сложность: O(1). */                                                    
//     EnumType(const EnumType& rhv)                                           
//       : value_(rhv.value_) {                                                  
//         assert(IsValidValue(rhv.value_));                                     
//     }                                                                         
//                                                                               
//     /* Сложность: O(1). */                                                    
//     EnumType& operator=(const EnumType& rhv) {                              
//         if ( this != &rhv ) {                                                 
//             assert(IsValidValue(rhv.value_));                                 
//             this->value_ = rhv.value_;                                        
//         }                                                                     
//         return *this;                                                         
//     }                                                                         
//                                                                               
//     /* Сложность: O(1).                 */                                    
//     friend bool operator==(const EnumType& lhv, const EnumType& rhv) {      
//         return lhv.value_ == rhv.value_;                                      
//     }                                                                         
//     /* Сложность: O(1). */                                                    
//     friend bool operator!=(const EnumType& lhv, const EnumType& rhv) {      
//         return !( lhv == rhv );                                               
//     }                                                                         
//     /* Сложность: O(1).                                */                     
//     friend bool operator<(const EnumType& lhv, const EnumType& rhv) {       
//         return lhv.value_ < rhv.value_;                                       
//     }                                                                         
//                                                                               
//     /* Преобразование объекта в целочисленное значение для сериализации. */   
//     /* Сложность: O(1).                                                  */   
//     const value_type& ToInt() const {                                         
//         return value_;                                                        
//     }                                                                         
//     /* Сложность: не более O(n) сравнений строк. */                           
//     const char* ToString() const {                                            
//         /* Преобразовываем тип value, а не всех case, но всегда все явно. */  
//         switch ( Impl(value_) ) {                                             
//             case kUndefined: { return "kUndefined"; }   
//             case A: { return "A"; }
//             case B: { return "B"; }
//             case C: { return "C"; }
//         }                                                                     
//         assert(false && "Unknown value");                                     
//         return NULL; /* недостижимо */                                        
//     }                                                                         
//                                                                               
//     /* Следующий (по порядку; циклически) элемент. */                         
//     /* Для kMax возвращается kMin.                 */                         
//     /* Сложность: O(1).                            */                         
//     EnumType Next() const {                                                  
//         return EnumType(Impl(value_ == kMax ? kMin : value_ + 1));           
//     }                                                                         
//     /* Предыдущий (по порядку; циклически) элемент. */                        
//     /* Для kMin возвращается kMax.                  */                        
//     /* Сложность: O(1).                             */                        
//     EnumType Prev() const {                                                  
//         return EnumType(Impl(value_ == kMin ? kMax : value_ - 1));           
//     }                                                                         
//                                                                               
//   private:                                                                    
//     /* Использование value_type позволяет гарантировать размер класса.  */    
//     value_type value_;                                                        
// }
//
//
//
// Рассмотрим макрос 
// LIBV_DEFINE_SMART_ENUM_USER_VALUES_STRICT(EnumType, 
//                                           ((A,1))((B,2))((C,4)), 
//                                           -1,
//                                           int8_t),
// аналогичный по смыслу enum { kUndefined = -1, A = 1, B = 2, C = 3 }.
//
// Внимание: значения задаются в паре скобок (синтаксис BOOST.PREPROCESSOR),
// не поддерживается указание только первого значения — явно указывать все.
//
// На этапе компиляции проверяется:
//  - отсутствие переполнений типа хранилища всеми переданными значениями
//  - отсутствие одинаковых значений.
//
// В отличие от LIBV_DEFINE_SMART_ENUM_STRICT пользователь должен явно задавать
// каждое значение, включая kUndefined (3 аргументом). kMin и kMax не создаются,
// так как не имеют смысла при возможно не ищущих подряд значениях.
//
// Код, генерируемый данным макросом, похож на представленный выше с учетом:
//  - метод IsValidValue и, как следствие, FromInt и конструктор от значения 
//    работают за O(n).
//  - методы Next и Prev работают за O(n).





#define LIBV_INTERNAL_SMART_ENUM_STRICT_TO_STRING_CASE(dummy,        \
                                                       n,            \
                                                       value_seq)    \
    case BOOST_PP_SEQ_ELEM(n, value_seq): {                          \
        return BOOST_PP_STRINGIZE(BOOST_PP_SEQ_ELEM(n, value_seq));  \
    }


#define LIBV_INTERNAL_SMART_ENUM_STRICT_FROM_STRING_IF(dummy,                  \
                                                       n,                      \
                                                       value_seq_arg_array) {  \
    const char* arg_name = BOOST_PP_ARRAY_ELEM(1, value_seq_arg_array);        \
    const char* enum_name = BOOST_PP_STRINGIZE(BOOST_PP_SEQ_ELEM(              \
        n, BOOST_PP_ARRAY_ELEM(0, value_seq_arg_array)));                      \
                                                                               \
    if ( !::std::strcmp(arg_name, enum_name) ) {                               \
        return BOOST_PP_SEQ_ELEM(n,BOOST_PP_ARRAY_ELEM(0,value_seq_arg_array));\
    }                                                                          \
}



///////////////////////////////////////



#define LIBV_DEFINE_SMART_ENUM_STRICT(type_name,                              \
                                      value_seq,                              \
                                      storage_type)                           \
class type_name {                                                             \
  /* В gcc BOOST_STATIC_ASSERT использует enum, имя которого генерируется   */\
  /* с использованием номера текущей строки.                                */\
  /* Но так как данный класс — макрос, то все BOOST_STATIC_ASSERT находятся */\
  /* на одной строке — где макрос используется. Поэтому объявляем фиктивные */\
  /* структуры, служащие пространствами имен для разделения ассертов.       */\
  struct static_assert_namespace_0 {                                          \
      /* Должен идти первым, т.к. только для intX определен integer_traits. */\
      BOOST_STATIC_ASSERT((boost::is_integral<storage_type>::value));         \
  };                                                                          \
                                                                              \
  struct static_assert_namespace_1 {                                          \
  /* Число элементов меньше максимального значения storage_type. */           \
      BOOST_STATIC_ASSERT((BOOST_PP_SEQ_SIZE(value_seq) > 0 &&                \
                           BOOST_PP_SEQ_SIZE(value_seq) <=                    \
                             boost::integer_traits<storage_type>::const_max));\
  };                                                                          \
                                                                              \
  public:                                                                     \
    typedef storage_type value_type;                                          \
                                                                              \
    enum Impl {                                                               \
        kUndefined,                                                           \
        kMin = kUndefined,                                                    \
        BOOST_PP_SEQ_ENUM(value_seq),                                         \
        kMax = BOOST_PP_SEQ_ELEM(BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(value_seq)),  \
                                 value_seq)                                   \
    };                                                                        \
                                                                              \
    /* Проверка принадлежности значения области допустимых значений. */       \
    /* Сложность: O(1).                                              */       \
    static bool IsValidValue(const value_type& value) {                       \
        return kMin <= value && value <= kMax;                                \
    }                                                                         \
                                                                              \
    /* Именованный конструктор из числа, принадлежащего ОДЗ. */               \
    /* Сложность: O(1).                                      */               \
    static type_name FromInt(const value_type& value) {                       \
        return type_name(Impl(value)); /* конструктор проверит предусловие */ \
    }                                                                         \
    /* Именованный конструктор из строки.        */                           \
    /* Для NULL возвращается kUndefined.         */                           \
    /* Поддерживаются значения kMin и kMax.      */                           \
    /* Сложность: не более O(n) сравнений строк. */                           \
    static type_name FromString(const char* str) {                            \
        if ( str == NULL || !::std::strcmp(str, "kUndefined") ) {             \
            return kUndefined;                                                \
        }                                                                     \
        /* ToString никогда не выдаст "kMin" и "kMax", */                     \
        /* но вдруг пользователь так захочет.          */                     \
        if ( !::std::strcmp(str, "kMin") ) {                                  \
            return kMin;                                                      \
        }                                                                     \
        if ( !::std::strcmp(str, "kMax") ) {                                  \
            return kMax;                                                      \
        }                                                                     \
        BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(value_seq),                         \
                        LIBV_INTERNAL_SMART_ENUM_STRICT_FROM_STRING_IF,       \
                        (2, (value_seq, str)))                                \
                                                                              \
        /* Ни один if не сработал. */                                         \
        LIBV_PRECOND_MSG(false, "Bad string value");                          \
        return type_name();  /* недостижимо */                                \
    }                                                                         \
                                                                              \
    /* Сложность: O(1). */                                                    \
    type_name()                                                               \
      : value_(kUndefined) {                                                  \
        assert(IsValidValue(value_));  /* для тестирования */                 \
    }                                                                         \
                                                                              \
    /* Не explicit для доступности всех операций между Impl и type_name. */   \
    /* Если пользователь библиотеки хочет создать enum из int, ему       */   \
    /* следует воспользоваться явным преобразованием через метод         */   \
    /* FromInt. Неявное преобразование запрещено, так как очень опасно,  */   \
    /* оно позволяет случайно передать перечисление *любого* типа в      */   \
    /* конструктор, что сводит на нет всю статическую типизацию класса.  */   \
    /* Сложность: O(1).                                                  */   \
    /*implicit*/ type_name(const Impl& value)                                 \
      : value_(static_cast<value_type>(value)) {                              \
        LIBV_PRECOND(IsValidValue(value_));                                   \
    }                                                                         \
                                                                              \
    /* Сложность: O(1). */                                                    \
    type_name(const type_name& rhv)                                           \
      : value_(rhv.value_) {                                                  \
        /* Значение rhv.value_ не может быть некорректным. */                 \
        /* Но для тестирования assert на всякий случай.    */                 \
        assert(IsValidValue(rhv.value_));                                     \
    }                                                                         \
                                                                              \
    /* Сложность: O(1). */                                                    \
    type_name& operator=(const type_name& rhv) {                              \
        if ( this != &rhv ) {                                                 \
            assert(IsValidValue(rhv.value_));                                 \
            this->value_ = rhv.value_;                                        \
        }                                                                     \
        return *this;                                                         \
    }                                                                         \
                                                                              \
    /* Для совместимости класса и Impl. */                                    \
    /* Сложность: O(1).                 */                                    \
    friend bool operator==(const type_name& lhv, const type_name& rhv) {      \
        return lhv.value_ == rhv.value_;                                      \
    }                                                                         \
    /* Сложность: O(1). */                                                    \
    friend bool operator!=(const type_name& lhv, const type_name& rhv) {      \
        return !( lhv == rhv );                                               \
    }                                                                         \
    /* operator< для возможности хранить в ::std::set. */                     \
    /* Сложность: O(1).                                */                     \
    friend bool operator<(const type_name& lhv, const type_name& rhv) {       \
        return lhv.value_ < rhv.value_;                                       \
    }                                                                         \
                                                                              \
    /* Преобразование объекта в целочисленное значение для сериализации. */   \
    /* Сложность: O(1).                                                  */   \
    const value_type& ToInt() const {                                         \
        return value_;                                                        \
    }                                                                         \
    /* Сложность: не более O(n) сравнений строк. */                           \
    const char* ToString() const {                                            \
        /* Преобразовываем тип value, а не всех case, но всегда все явно. */  \
        switch ( Impl(value_) ) {                                             \
            case kUndefined: { return "kUndefined"; }                         \
            BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(value_seq),                     \
                            LIBV_INTERNAL_SMART_ENUM_STRICT_TO_STRING_CASE,   \
                            value_seq)                                        \
        }                                                                     \
        assert(false && "Unknown value");                                     \
        return NULL; /* недостижимо */                                        \
    }                                                                         \
                                                                              \
    /* Следующий (по порядку; циклически) элемент. */                         \
    /* Для kMax возвращается kMin.                 */                         \
    /* Сложность: O(1).                            */                         \
    type_name Next() const {                                                  \
        return type_name(Impl(value_ == kMax ? kMin : value_ + 1));           \
    }                                                                         \
    /* Предыдущий (по порядку; циклически) элемент. */                        \
    /* Для kMin возвращается kMax.                  */                        \
    /* Сложность: O(1).                             */                        \
    type_name Prev() const {                                                  \
        return type_name(Impl(value_ == kMin ? kMax : value_ - 1));           \
    }                                                                         \
                                                                              \
  private:                                                                    \
    /* Использование value_type позволяет гарантировать размер класса.  */    \
    value_type value_;                                                        \
}



///////////////////////////////////////



#define LIBV_DEFINE_SMART_ENUM(type_name,     \
                               value_seq)     \
    LIBV_DEFINE_SMART_ENUM_STRICT(type_name,  \
                                  value_seq,  \
                                  int)





////////////////////////////////////////////////////////////////////////////////





// Проверка, что все пользовательские значения умещаются в заданный тип.
#define LIBV_INTERNAL_SMART_ENUM_UV_NO_OVERLOW(dummy,                          \
                                               n,                              \
                                               nv_tuple_seq_undef_value_array) \
    BOOST_PP_IF(n, &&, true &&)  /* BOOST_PP_EMPTY не заработал на gcc. */     \
    BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_SEQ_ELEM(                               \
        n, BOOST_PP_ARRAY_ELEM(0, nv_tuple_seq_undef_value_array))) >=         \
    boost::integer_traits<                                                     \
        BOOST_PP_ARRAY_ELEM(1, nv_tuple_seq_undef_value_array)>::const_min     \
    &&                                                                         \
    BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_SEQ_ELEM(                               \
        n, BOOST_PP_ARRAY_ELEM(0, nv_tuple_seq_undef_value_array))) <=         \
    boost::integer_traits<                                                     \
        BOOST_PP_ARRAY_ELEM(1, nv_tuple_seq_undef_value_array)>::const_max



// Проверка, что ни одно пользовательское значение не равно kUndefined.
#define LIBV_INTERNAL_SMART_ENUM_UV_NE_TO_U_VAL(dummy,                         \
                                                n,                             \
                                                nv_tuple_seq_undef_value_array)\
    BOOST_PP_IF(n, &&, true &&)                                                \
    BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_SEQ_ELEM(                               \
        n, BOOST_PP_ARRAY_ELEM(0, nv_tuple_seq_undef_value_array))) !=         \
    BOOST_PP_ARRAY_ELEM(1, nv_tuple_seq_undef_value_array)


#define LIBV_INTERNAL_SMART_ENUM_UV_ENUM_ELEMENT(dummy,                      \
                                                 n,                          \
                                                 name_value_tuple_seq)       \
    BOOST_PP_COMMA_IF(n)                                                     \
    BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(n, name_value_tuple_seq)) =  \
    BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_SEQ_ELEM(n, name_value_tuple_seq))


#define LIBV_INTERNAL_SMART_ENUM_UV_IS_VALID_VALUE_CASE(dummy,                 \
                                                        n,                     \
                                                        name_value_tuple_seq)  \
    case BOOST_PP_TUPLE_ELEM(                                                  \
            2, 0, BOOST_PP_SEQ_ELEM(n, name_value_tuple_seq)): {               \
        return true;                                                           \
    }


#define LIBV_INTERNAL_SMART_ENUM_UV_FROM_INT_CASE(dummy,                 \
                                                  n,                     \
                                                  name_value_tuple_seq)  \
    case BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(                    \
            n, name_value_tuple_seq)): {                                 \
        return BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(              \
                   n, name_value_tuple_seq));                            \
    }


#define LIBV_INTERNAL_SMART_ENUM_UV_FROM_STRING_IF(dummy,                     \
                                                   n,                         \
                                                   nv_tuple_seq_arg_array) {  \
    const char* arg_name = BOOST_PP_ARRAY_ELEM(1, nv_tuple_seq_arg_array);    \
    const char* enum_name = BOOST_PP_STRINGIZE(                               \
        BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(                          \
            n, BOOST_PP_ARRAY_ELEM(0, nv_tuple_seq_arg_array))));             \
    if ( !::std::strcmp(arg_name, enum_name) ) {                              \
        return BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(                   \
                   n, BOOST_PP_ARRAY_ELEM(0, nv_tuple_seq_arg_array)));       \
    }                                                                         \
}


#define LIBV_INTERNAL_SMART_ENUM_UV_TO_STRING_CASE(dummy,                 \
                                                   n,                     \
                                                   name_value_tuple_seq)  \
    case BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(                     \
             n, name_value_tuple_seq)): {                                 \
        return BOOST_PP_STRINGIZE(                                        \
                   BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(           \
                       n, name_value_tuple_seq)));                        \
    }


#define LIBV_INTERNAL_SMART_ENUM_UV_NEXT_CASE(dummy,                 \
                                              n,                     \
                                              name_value_tuple_seq)  \
    case BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(                \
             n, name_value_tuple_seq)): {                            \
        return BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(          \
                   BOOST_PP_INC(n), name_value_tuple_seq));          \
    }


#define LIBV_INTERNAL_SMART_ENUM_UV_PREV_CASE(dummy,                 \
                                              n,                     \
                                              name_value_tuple_seq)  \
    case BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(                \
             n, name_value_tuple_seq)): {                            \
        return BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(          \
                   BOOST_PP_DEC(n), name_value_tuple_seq));          \
    }



///////////////////////////////////////



#define LIBV_DEFINE_SMART_ENUM_USER_VALUES_STRICT(type_name,                   \
                                                  name_value_tuple_seq,        \
                                                  undefined_value,             \
                                                  storage_type)                \
class type_name {                                                              \
  struct static_assert_namespace_0 {                                           \
      BOOST_STATIC_ASSERT((boost::is_integral<storage_type>::value));          \
  };                                                                           \
  struct static_assert_namespace_1 {                                           \
      /* Число элементов больше нуля, хотя по-другому и не получится. */       \
      BOOST_STATIC_ASSERT(BOOST_PP_SEQ_SIZE(name_value_tuple_seq) > 0);        \
  };                                                                           \
  struct static_assert_namespace_2 {                                           \
      /* Значения всех элементов лежат в допустимых значениях storage_type. */ \
      BOOST_STATIC_ASSERT(BOOST_PP_REPEAT(                                     \
                              BOOST_PP_SEQ_SIZE(name_value_tuple_seq),         \
                              LIBV_INTERNAL_SMART_ENUM_UV_NO_OVERLOW,          \
                              (2, (name_value_tuple_seq, storage_type))));     \
  };                                                                           \
  struct static_assert_namespace_3 {                                           \
      /* undefined_value лежит в допустимых значениях storage_type. */         \
      BOOST_STATIC_ASSERT(                                                     \
        undefined_value >= boost::integer_traits<storage_type>::const_min &&   \
        undefined_value <= boost::integer_traits<storage_type>::const_max);    \
  };                                                                           \
  struct static_assert_namespace_4 {                                           \
      /* Значения всех элементов не равны undefined_value. */                  \
      BOOST_STATIC_ASSERT(BOOST_PP_REPEAT(                                     \
                              BOOST_PP_SEQ_SIZE(name_value_tuple_seq),         \
                              LIBV_INTERNAL_SMART_ENUM_UV_NE_TO_U_VAL,         \
                              (2, (name_value_tuple_seq, undefined_value))));  \
  };                                                                           \
                                                                               \
  public:                                                                      \
    typedef storage_type value_type;                                           \
                                                                               \
    enum Impl {                                                                \
        kUndefined = undefined_value,                                          \
        BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(name_value_tuple_seq),               \
                        LIBV_INTERNAL_SMART_ENUM_UV_ENUM_ELEMENT,              \
                        name_value_tuple_seq)                                  \
    };                                                                         \
                                                                               \
    /* Проверка принадлежности значения области допустимых значений. */        \
    /* Сложность: не более O(n) сравнений значений.                  */        \
    static bool IsValidValue(const value_type& value) {                        \
        switch ( Impl(value) ) {                                               \
            case kUndefined: { return true; }                                  \
            BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(name_value_tuple_seq),           \
                            LIBV_INTERNAL_SMART_ENUM_UV_IS_VALID_VALUE_CASE,   \
                            name_value_tuple_seq)                              \
        }                                                                      \
        return false;                                                          \
    }                                                                          \
                                                                               \
    /* Именованный конструктор из числа, принадлежащего ОДЗ. */                \
    /* Сложность: не более O(n) сравнений значений.          */                \
    static type_name FromInt(const value_type& value) {                        \
        switch ( Impl(value) ) {                                               \
            case kUndefined: { return kUndefined; }                            \
            BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(name_value_tuple_seq),           \
                            LIBV_INTERNAL_SMART_ENUM_UV_FROM_INT_CASE,         \
                            name_value_tuple_seq)                              \
        }                                                                      \
        LIBV_PRECOND_MSG(false, "Bad value");                                  \
        return type_name();  /* недостижимо */                                 \
    }                                                                          \
    /* Именованный конструктор из строки.        */                            \
    /* Для NULL возвращается kUndefined.         */                            \
    /* Сложность: не более O(n) сравнений строк. */                            \
    static type_name FromString(const char* str) {                             \
        if ( str == NULL || !::std::strcmp(str, "kUndefined") ) {              \
            return kUndefined;                                                 \
        }                                                                      \
        BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(name_value_tuple_seq),               \
                        LIBV_INTERNAL_SMART_ENUM_UV_FROM_STRING_IF,            \
                        (2, (name_value_tuple_seq, str)))                      \
                                                                               \
        /* Ни один if не сработал. */                                          \
        LIBV_PRECOND_MSG(false, "Bad string value");                           \
        return type_name();  /* недостижимо */                                 \
    }                                                                          \
                                                                               \
    /* Сложность: O(1). */                                                     \
    type_name()                                                                \
      : value_(kUndefined) {                                                   \
        assert(IsValidValue(value_));                                          \
    }                                                                          \
                                                                               \
    /* Не explicit, для доступности всех операций между Impl и type_name. */   \
    /* Сложность: не более O(n) сравнений значений.                       */   \
    /*implicit*/ type_name(const Impl& value)                                  \
      : value_(static_cast<value_type>(value)) {                               \
        LIBV_PRECOND(IsValidValue(value_));                                    \
    }                                                                          \
    /* Сложность: O(1). */                                                     \
    type_name(const type_name& rhv)                                            \
      : value_(rhv.value_) {                                                   \
        assert(IsValidValue(rhv.value_));                                      \
    }                                                                          \
                                                                               \
    /* Сложность: O(1). */                                                     \
    type_name& operator=(const type_name& rhv) {                               \
        if ( this != &rhv ) {                                                  \
            assert(IsValidValue(rhv.value_));                                  \
            this->value_ = rhv.value_;                                         \
        }                                                                      \
        return *this;                                                          \
    }                                                                          \
                                                                               \
    /* Сложность: O(1). */                                                     \
    friend bool operator==(const type_name& lhv, const type_name& rhv) {       \
        return lhv.value_ == rhv.value_;                                       \
    }                                                                          \
    /* Сложность: O(1) */                                                      \
    friend bool operator!=(const type_name& lhv, const type_name& rhv) {       \
        return !( lhv == rhv );                                                \
    }                                                                          \
    /* Сложность: O(1). */                                                     \
    friend bool operator<(const type_name& lhv, const type_name& rhv) {        \
        return lhv.value_ < rhv.value_;                                        \
    }                                                                          \
                                                                               \
    /* Преобразование объекта в целочисленное значение для сериализации. */    \
    /* Сложность: O(1).                                                  */    \
    value_type ToInt() const {                                                 \
        return value_;                                                         \
    }                                                                          \
    /* Сложность: не более O(n) сравнений строк. */                            \
    const char* ToString() const {                                             \
        switch ( Impl(value_) ) {                                              \
            case kUndefined: { return "kUndefined"; }                          \
            BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(name_value_tuple_seq),           \
                            LIBV_INTERNAL_SMART_ENUM_UV_TO_STRING_CASE,        \
                            name_value_tuple_seq)                              \
        }                                                                      \
        assert(false && "Unknown value");                                      \
        return NULL;  /* недостижимо */                                        \
    }                                                                          \
                                                                               \
    /* Следующий (в порядке задания при определении; циклически) элемент. */   \
    /* Для последнего возвращается kUndefined. Для kUndefined — нулевой.  */   \
    /* Сложность: не более O(n) сравнений значений.                       */   \
    type_name Next() const {                                                   \
        switch ( Impl(value_) ) {                                              \
            case kUndefined: {                                                 \
                return BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(            \
                           0, name_value_tuple_seq));                          \
            }                                                                  \
            case BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(                  \
                     BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(name_value_tuple_seq)),    \
                     name_value_tuple_seq)): {                                 \
               return kUndefined;                                              \
            }                                                                  \
            BOOST_PP_REPEAT(BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(                    \
                                name_value_tuple_seq)),                        \
                            LIBV_INTERNAL_SMART_ENUM_UV_NEXT_CASE,             \
                            name_value_tuple_seq)                              \
        }                                                                      \
        assert(false && "Unknown value");                                      \
        return type_name();  /* недостижимо */                                 \
    }                                                                          \
    /* Предыдущий (в порядке задания при определении; циклически) элемент. */  \
    /* Для последнего возвращается kUndefined. Для нулевого — kUndefined.  */  \
    /* Сложность: не более O(n) сравнений значений.                        */  \
    type_name Prev() const {                                                   \
        switch ( Impl(value_) ) {                                              \
            case kUndefined: {                                                 \
               return BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(             \
                          BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(                      \
                              name_value_tuple_seq)),                          \
                          name_value_tuple_seq));                              \
            }                                                                  \
            case BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(                  \
                     0, name_value_tuple_seq)): {                              \
                return kUndefined;                                             \
            }                                                                  \
            BOOST_PP_REPEAT_FROM_TO(1,                                         \
                                    BOOST_PP_SEQ_SIZE(name_value_tuple_seq),   \
                                    LIBV_INTERNAL_SMART_ENUM_UV_PREV_CASE,     \
                                    name_value_tuple_seq)                      \
        }                                                                      \
        assert(false && "Unknown value");                                      \
        return type_name();  /* недостижимо */                                 \
    }                                                                          \
                                                                               \
  private:                                                                     \
    /* Использование value_type позволяет гарантировать размер класса. */      \
    value_type value_;                                                         \
}



///////////////////////////////////////



#define LIBV_DEFINE_SMART_ENUM_USER_VALUES(type_name,                \
                                           name_value_tuple_seq,     \
                                           undefined_value)          \
    LIBV_DEFINE_SMART_ENUM_USER_VALUES_STRICT(type_name,             \
                                              name_value_tuple_seq,  \
                                              undefined_value,       \
                                              int)

#endif // LIBV_BOOST_SMART_ENUM_H_